﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using SoundEngine;

namespace SpEasy
{
    public partial class frmMain : Form
    {
        private TcpClient m_TcpClient;
        private IPEndPoint m_EndPoint;
        private Boolean m_Send = false;

        private WaveInRecorder m_Recorder;
        private WaveOutPlayer m_Player;
        private FifoStream m_Fifo = new FifoStream();
        private Thread m_ListenerThread;
        private Boolean m_Listen = true;

        private Byte[] m_PlayBuffer;
        private Byte[] m_RecBuffer;

        public frmMain()
        {
            InitializeComponent();
            this.txtIp.Text = Settings.Default.Host;
            this.txtUsername.Text = Environment.UserName;
        }

        private void btnConnect_Click(Object sender, EventArgs e)
        {
            IPAddress ipAddress;
            try
            {
                ipAddress = IPAddress.Parse(this.txtIp.Text);
                m_EndPoint = new IPEndPoint(ipAddress, 1068);
            }
            catch (FormatException ex)
            {
                try
                {
                    IPHostEntry hostInfo = Dns.GetHostByName(this.txtIp.Text);
                    ipAddress = hostInfo.AddressList[0];
                    m_EndPoint = new IPEndPoint(ipAddress, 1068);
                }
                catch
                {
                }
            }
            catch (ArgumentNullException ex)
            {
                MessageBox.Show(ex.Message, "Du måste ställa in server att ansluta till.");
            }
            try
            {

                m_TcpClient = new TcpClient();

                lstMessages.Items.Add("Ansluter till " + txtIp.Text + ":1068");
                txtUsername.Enabled = false;
                txtIp.Enabled = false;

                WaveFormat fmt = new WaveFormat(11025, 16, 1);
                m_Player = new WaveOutPlayer(-1, fmt, 16384, 3, new BufferFillEventHandler(Filler));
                m_Recorder = new WaveInRecorder(-1, fmt, 16384, 3, new BufferDoneEventHandler(DataArrived));
                btnConnect.Enabled = false;

                m_TcpClient.Connect(m_EndPoint);

                m_ListenerThread = new Thread(new ThreadStart(TcpListener));
                m_ListenerThread.Start();

                if (m_TcpClient.Connected)
                {
                    lstMessages.Items.Add("Anslutning lyckades");
                    m_TcpClient.Client.Send(Encoding.UTF8.GetBytes("msgSetUsername" + txtUsername.Text + "\r\n"));
                    //m_TcpClient.Client.Send(Encoding.UTF8.GetBytes("msgGetUsers\r\n"));
                }
                else
                {
                    lstMessages.Items.Add("Anslutningen misslyckades!");
                }
            }
            catch
            {
                AM("Kunde inte ansluta till " + txtIp.Text);
                txtIp.Enabled = true;
                txtUsername.Enabled = true;
                btnConnect.Enabled = true;
            }
        }

        private void TcpListener()
        {
            Byte[] msgBytes = Encoding.UTF8.GetBytes("msg");

            try
            {
                while (m_Listen)
                {
                    Byte[] buffer = new Byte[m_TcpClient.ReceiveBufferSize];
                    m_TcpClient.Client.Receive(buffer);

                    //
                    // check if is msg
                    if (
                        buffer[0] == msgBytes[0]
                        &&
                        buffer[1] == msgBytes[1]
                        &&
                        buffer[2] == msgBytes[2])
                    {
                        HandleMessage(buffer);
                    }
                    else
                    {
                        Byte[] decodedBuffer = new Byte[buffer.Length * 2];
                        SoundCodec.Law.ALawDecoder.ALawDecode(buffer, out decodedBuffer);
                        m_Fifo.Write(decodedBuffer, 0, decodedBuffer.Length);
                    }
                }
            }
            catch (Exception ex)
            {
                AM("Bröt med felmeddelande; " + ex.Message);
                if (m_TcpClient != null && m_TcpClient.Client != null && m_TcpClient.Connected)
                {
                    AM("Startar om lyssnarN!");
                    TcpListener();
                }
            }
        }

        private void HandleMessage(Byte[] buff)
        {
            String[] message = Encoding.UTF8.GetString(buff).Replace("\x00", "").Split("\r\n".ToCharArray());

            foreach (String msg in message)
            {
                AM("medd: " + msg);
                if (msg.Replace("\r\n", "").Trim() != "")
                {
                    HandleMessage(msg.Replace("\r\n", "").Remove(0, 3));
                }
            }
        }

        private delegate void MethodInvoker();
        private void HandleMessage(String message)
        {
            if (message.StartsWith("GetUsers"))
            {
                treeServer.Nodes.Clear();
                message = message.Remove(0, "GetUsers".Length);
                String[] users = message.Split(",".ToCharArray());

                foreach (String u in users)
                {
                    if (!String.IsNullOrEmpty(u))
                    {
                        this.Invoke((MethodInvoker)delegate()
                        {
                            treeServer.Nodes.Add(u);
                        });
                    }
                }
            }

            AM(message);
        }

        private void Filler(IntPtr data, int size)
        {
            if (m_PlayBuffer == null || m_PlayBuffer.Length < size)
                m_PlayBuffer = new byte[size];
            if (m_Fifo.Length >= size)
                m_Fifo.Read(m_PlayBuffer, 0, size);
            else
                for (int i = 0; i < m_PlayBuffer.Length; i++)
                    m_PlayBuffer[i] = 0;

            System.Runtime.InteropServices.Marshal.Copy(m_PlayBuffer, 0, data, size);
        }

        private void DataArrived(IntPtr data, int size)
        {
            if (m_RecBuffer == null || m_RecBuffer.Length < size)
                m_RecBuffer = new byte[size];
            System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);

            if (m_Send)
            {
                Byte[] sendBuffer = SoundCodec.Law.ALawEncoder.ALawEncode(m_RecBuffer);
                AM("Sending " + sendBuffer.Length + " bytes");
                m_TcpClient.Client.Send(sendBuffer);
            }
        }

        private void frmMain_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.ControlKey || e.KeyData == Keys.Control || e.Control)
            {
                m_Send = true;
                Text = "Sending";
            }
        }

        private void frmMain_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.ControlKey)
            {
                m_Send = false;
                Text = "Not sending";
            }
        }

        private delegate void AMC(String t);
        public void AM(String t)
        {
            if (this.lstMessages.InvokeRequired)
            {
                AMC d = new AMC(AM);
                this.Invoke(d, new object[] { t });
            }
            else
            {
                lstMessages.Items.Add(t);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            m_TcpClient.Client.Send(Encoding.UTF8.GetBytes("msgHej från dej"));
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (m_ListenerThread != null)
            {
                m_Listen = false;
                m_ListenerThread.Abort();
            }
        }
    }
}
